'use strict';


const fs = require('fs')
const path = require('path')
const inquirer = require('inquirer')
const fse = require('fs-extra')
const semver = require('semver')
const userHome = require('user-home')
const ejs = require('ejs')
const glob = require('glob')
const Command = require('@ayuan-cli/command')
const Package = require('@ayuan-cli/package')
const log = require('@ayuan-cli/log')
const {spinnerStart, sleep, spawnAsync} = require('@ayuan-cli/util')
// const getProjectTemplate = require('./getProjectTemplate')

const TYPE_PROJECT = 'project'
const TYPE_COMPONENT = 'component'
const TYPE_TEMPLATE_NORMAL = 'normal'
const TYPE_TEMPLATE_CUSTOM = 'custom'
const WHITE_COMMAND = ['npm', 'cnpm'] // 命令白名单

class InitCommand extends Command {
    init() {
        this.createDirNam = this._argv[0] || ''
        this.force = !!this._argv[1].force
        log.verbose('createDirNam', this.createDirNam)
        log.verbose('force', this.force)
    }

    async exec() {
        try {
            const createDirNam = this.createDirNam || '/'
            this.createPath = path.join(process.cwd(), createDirNam)
            if (createDirNam) { // 判断是否存在文件夹 创建文件夹，进入文件夹
                fse.ensureDirSync(this.createDirNam)
                await this.start()
            } else { // 询问时候在当前目录进行初始化项目
                const {initProject} = await inquirer.prompt({
                    type: 'confirm',
                    name: 'initProject',
                    default: false,
                    message: `是否确认在当前目录下进行初始化(${process.cwd()})？`,
                })
                if (initProject) {
                    await this.start()
                }
            }
        } catch (e) {
            log.error(e)
            if (process.env.LOG_LEVEL === 'verbose') {
                console.log(e)
            }
        }
    }


    async prepare() {
        // 判断项目模板是否存在
        // const template = await getProjectTemplate()
        // if (!template || template.length === 0) {
        //     throw new Error('项目模板不存在')
        // }
        // this.template = template
        // 判断当前目录是否为空
        this.template = [
            {
                name: 'vue2标准模板',
                value: 'ayuan-cli-template-vue2',
                npmName: 'ayuan-cli-template-vue2',
                version: '1.4.0',
                type: 'project',
                projectType: 'normal',
                installCommand: 'npm install',
                startCommand: 'npm run serve',
                ignore: ['**/node_modules/**', '**/public/**'] // 忽略 ejs 渲染
            },
            {
                name: 'vue3标准模板',
                value: 'ayuan-cli-template-vue3',
                npmName: 'ayuan-cli-template-vue3',
                version: '1.0.0',
                type: 'project',
                projectType: 'normal',
                installCommand: 'npm install',
                startCommand: 'npm run serve',
                ignore: ['**/node_modules/**', '**/public/**']
            },
            {
                name: 'react17标准模板',
                value: 'ayuan-cli-template-react17',
                npmName: 'ayuan-cli-template-react17',
                version: '1.0.0',
                type: 'project',
                projectType: 'normal',
                installCommand: 'npm install',
                startCommand: 'npm run start',
                ignore: ['**/node_modules/**', '**/public/**']
            },
            {
                name: '大屏组件库',
                value: 'ayuan-cli-template-datav',
                npmName: 'ayuan-cli-template-datav',
                version: '1.1.0',
                type: 'component',
                projectType: '',
                installCommand: 'npm install',
                startCommand: '',
                ignore: ['**/node_modules/**', '**/public/**']
            }

        ]
        const localPath = process.cwd()
        const createDirNam = this.createDirNam
        if (!this.isDirEmpty(localPath) && !createDirNam) {
            let isContinue = false
            if (!this.force) {
                // 询问是否继续创建
                isContinue = (await inquirer.prompt({
                    type: 'confirm',
                    name: 'isContinue',
                    default: false,
                    message: '当前文件夹不为空，是否继续创建项目？',
                })).isContinue
                if (!isContinue) {
                    return
                }
            }
            // 是否启动强制更新
            if (isContinue || this.force) {
                // 给用户做二次确认
                const {confirmDelete} = await inquirer.prompt({
                    type: 'confirm',
                    name: 'confirmDelete',
                    default: false,
                    message: '是否确认清空当前目录下的文件？',
                })
                if (confirmDelete) {
                    // 清空当前目录
                    fse.emptyDirSync(localPath)
                }
            }
        } else {
            const createPath = this.createPath
            if (!this.isDirEmpty(createPath)) {
                const {confirmCover} = await inquirer.prompt({
                    type: 'confirm',
                    name: 'confirmCover',
                    default: false,
                    message: '当前创建目录不为空,是否确认覆盖当前目录下已存在的文件？',
                })
                if (!confirmCover) {
                    return
                }
            }
        }
        // 获取返回信息
        return this.getProjectInfo()
    }

    async start() {
        // 准备阶段
        const projectInfo = await this.prepare()
        if (projectInfo) {
            // 下载模板
            log.verbose('projectInfo', projectInfo)
            this.projectInfo = projectInfo
            // 处理模板路径
            await this.downloadTemplate()
            // 安装模板
            await this.installTemplate()
        }
    }

    async getProjectInfo() {
        let projectInfo = {}
        // 1. 选择创建项目或组件
        const {type} = await inquirer.prompt({
            type: 'list',
            name: 'type',
            message: '请选择初始化类型',
            default: TYPE_PROJECT,
            choices: [{
                name: '项目',
                value: TYPE_PROJECT,
            }, {
                name: '组件',
                value: TYPE_COMPONENT,
            }],
        })
        log.verbose('type', type)
        const template = this.template
        const choices = template.filter(item => item.type === type)
        // 2. 获取项目的基本信息
        const title = type === TYPE_PROJECT ? '项目' : '组件'
        const info = await inquirer.prompt([
            {
                type: 'input',
                name: 'name',
                message: `请输入${title}名称`,
                default: this.createDirNam,
                validate: function (v) {
                    const done = this.async()
                    setTimeout(function () {
                        // 1.首字符必须为英文字符
                        // 2.尾字符必须为英文或数字，不能为字符
                        // 3.字符仅允许"-_"
                        if (!/^[a-zA-Z]+([-][a-zA-Z][a-zA-Z0-9]*|[_][a-zA-Z][a-zA-Z0-9]*|[a-zA-Z0-9])*$/.test(v)) {
                            done(`${title}名称应为英文开头，可以包含英文、数数字、"—"、"_"`)
                            return
                        }
                        done(null, true)
                    }, 0)
                },
                filter: function (v) {
                    return v
                },
            }, {
                type: 'input',
                name: 'version',
                message: `请输入${title}版本号`,
                default: '1.0.0',
                validate: function (v) {
                    const done = this.async()
                    setTimeout(function () {
                        if (!(!!semver.valid(v))) {
                            done('请输入合法的版本号，例如 1.0.0')
                            return
                        }
                        done(null, true)
                    }, 0)
                },
                filter: function (v) {
                    if (!!semver.valid(v)) {
                        return semver.valid(v)
                    } else {
                        return v
                    }
                },
            }, {
                type: 'list',
                name: 'template',
                message: '请选择项目模板',
                // choices: this.createTemplateChoice(),
                choices,
            },
        ])
        projectInfo = {
            type,
            ...info,
        }
        if (projectInfo.name) {
            projectInfo.name = require('kebab-case')(projectInfo.name).replace(/^-/, '')
        }
        return projectInfo
    }

    isDirEmpty(localPath) {
        let fileList = fs.readdirSync(localPath)
        // 文件过滤的逻辑
        fileList = fileList.filter(file => (
            !file.startsWith('.') && ['node_modules'].indexOf(file) < 0
        ))
        return !fileList || fileList.length <= 0
    }

    async downloadTemplate() {
        const {template} = this.projectInfo
        const templateInfo = this.template.find(item => item.npmName === template)
        this.templateInfo = templateInfo
        log.verbose('templateInfo', templateInfo)
        const targetPath = path.resolve(userHome, '.ayuan-cli', 'template')
        const storeDir = path.resolve(userHome, '.ayuan-cli', 'template', 'node_modules')
        const {npmName, version} = templateInfo
        const templateNpm = new Package({
            targetPath,
            storeDir,
            packageName: npmName,
            packageVersion: version,
        })
        this.templateNpm = templateNpm
        if (!await templateNpm.exists()) {
            const spinner = spinnerStart('正在下载模板...')
            await sleep()
            try {
                await templateNpm.install()
                spinner.stop(true)
                log.success('下载模板成功')
            } catch (e) {
                spinner.stop(true)
                throw e
            }
        } else {
            const spinner = spinnerStart('正在更新模板...')
            await sleep()
            try {
                await templateNpm.update()
                spinner.stop(true)
                log.success('更新模板成功')
            } catch (e) {
                spinner.stop(true)
                throw e
            }
        }
    }

    async installTemplate() {
        const template = this.template
        if (template) {
            if (!template.type) template.type = TYPE_TEMPLATE_NORMAL
            if (template.type === TYPE_TEMPLATE_NORMAL) { // 标准安装
                await this.installNormalTemplate()
            } else if (template.type === TYPE_TEMPLATE_CUSTOM) { // 自定义安装
                // this.installCustomTemplate()
            }
        } else {
            throw new Error('项目模板信息不存在')
        }
    }

    async installNormalTemplate() {
        const createPath = this.createPath
        const templateInfo = this.templateInfo
        let spinner = spinnerStart('正在安装模板...')
        await sleep()
        try {
            const cacheFilePath = path.join(this.templateNpm.cacheFilePath, 'template')
            fse.ensureDirSync(cacheFilePath)
            fse.ensureDirSync(createPath)
            log.verbose('node_modules 拷贝文件路径', cacheFilePath)
            fse.copySync(cacheFilePath, createPath)
            spinner.stop(true)
            log.success('安装模板成功')
        } catch (e) {
            spinner.stop(true)
            throw e
        }
        await this.ejsRender(templateInfo)
        const {installCommand, startCommand} = this.templateInfo;
        // 依赖安装
        await this.execCommand(installCommand, '依赖安装失败！', createPath);
        // 启动命令执行
        await this.execCommand(startCommand, '启动执行命令失败！', createPath);
    }

    // installCustomTemplate(){
    // }

    checkCommand(cmd) {
        if (WHITE_COMMAND.includes(cmd)) {
            return cmd;
        }
        return null;
    }

    async execCommand(command, errMsg, createPath) {
        let ret;
        if (command) {
            const cmdArray = command.split(' ');
            const cmd = this.checkCommand(cmdArray[0]);
            if (!cmd) {
                throw new Error('命令不存在！命令：' + command);
            }
            const args = cmdArray.slice(1);
            ret = await spawnAsync(cmd, args, {
                stdio: 'inherit',
                cwd: createPath,
            });
        }
        if (ret !== 0) {
            throw new Error(errMsg);
        }
        return ret;
    }

    ejsRender(options) {
        const createPath = this.createPath
        const cwd = process.cwd()
        const curFileName = createPath.substring(createPath.lastIndexOf('/') + 1)
        return new Promise((resolve, reject) => {
            glob(`**/${curFileName}/**`, {
                cwd,
                ignore: options.ignore || '',
                nodir: true
            }, (err, files) => {
                if (err) {
                    reject(err)
                }
                Promise.all(files.map(file => {
                    const filePath = path.join(cwd, file)
                    return new Promise((res, rej) => {
                        ejs.renderFile(filePath, this.projectInfo, {}, (err, str) => {
                            if (err) {
                                rej(err)
                            } else {
                                // 重写文件
                                fse.writeFileSync(filePath, str)
                                res(str)
                            }
                        })
                    })
                })).then(() => {
                    resolve()
                }).catch((err) => {
                    reject(err)
                })
            })
        })
    }

    // createTemplateChoice() {
    //     return this.template.map(item => ({
    //         value: item.npmName,
    //         name: item.name,
    //     }))
    // }
}


function init() {
    return new InitCommand([...arguments])
}

module.exports = init
module.exports.InitCommand = InitCommand
